package com.gitee.apanlh.util.image;

import com.gitee.apanlh.exp.ImageReadException;
import com.gitee.apanlh.exp.ImageWriteException;
import com.gitee.apanlh.util.id.IdUtils;
import com.gitee.apanlh.util.io.IOUtils;

import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;

/**		
 * 	图片工具类
 * 	压缩等
 *
 * 	@author Pan
 */
public class ImageUtils {
	
	/** 相关格式 */
	public static final String JPG_SUFFIX  = "jpg";
	public static final String PNG_SUFFIX  = "png";
	public static final String BMP_SUFFIX  = "bmp";
	public static final String GIF_SUFFIX  = "gif";
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private ImageUtils() {
		//	不允许外部实例
		super();
	}
	
    /**
     * 	宽度
     */
    private static final int WIDTH = 180;
    /**
     * 	高度
     */
    private static final int HEIGHT = 120;
    /**
     * 	水印透明度
     */
    private static final float ALPHA = 1.0f;
    /**
     * 	水印文字大小
     */
    private static final int FONT_SIZE = 12;
    /**
     * 	水印文字体
     */
    private static final Font FONT = new Font("宋体", Font.PLAIN, FONT_SIZE);
    /**
     * 	水印文字颜色	灰色
     */
    private static final Color COLOR = new Color(128, 138, 135);
	
    /**	
     * 	获取本地的所有字体
     * 	
     * 	@author Pan
     * 	@return	String[]
     */
    public static String[] getSystemFont() {
    	return GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    }
    
    /**	
     * 	宽度为默认300px;
     * 	<br>高度120px;
     * 	<br>默认 宋体
     * 	<br>水印横向50
     * 	<br>纵向30
     * 	<br>颜色默认灰色
     * 	
     * 	@author Pan
     * 	@param  os			输出流
     * 	@param  arguments	输出参数
     * 	@return	boolean
     */
    public static boolean createWatermarkImage(OutputStream os, String... arguments) {
    	BufferedImage bufferedImage;
    	Graphics2D graphics2d = null;
    	try {
    		 // 1.创建空白图片
            bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
            // 2.获取图片画笔
            graphics2d = bufferedImage.createGraphics();
            bufferedImage = graphics2d.getDeviceConfiguration().createCompatibleImage(WIDTH, HEIGHT, Transparency.TRANSLUCENT);
            graphics2d.dispose();
            graphics2d =  bufferedImage.createGraphics();
            
            // 7、设置对线段的锯齿状边缘处理
            graphics2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            // 8、设置水印旋转
            graphics2d.rotate(Math.toRadians(-10), (double) bufferedImage.getWidth() / 2, (double) bufferedImage.getHeight() / 2);
            // 9、设置水印文字颜色
            graphics2d.setColor(COLOR);
            // 10、设置水印文字Font
            graphics2d.setFont(FONT);
            // 11、设置水印文字透明度
            graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA));
            graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            // 水印横向位置
            int positionWidth = 10;
            // 水印纵向位置
            int positionHeight = 30;
            // 12、水印位置
            createWatermarkImagePosition(graphics2d, positionWidth, positionHeight, arguments);
		} finally {
			// 13、释放资源
			if (graphics2d != null) {
				graphics2d.dispose();
			}
		}
		return write(bufferedImage, PNG_SUFFIX, os);
    }
    
    /**	
     * 	宽度为默认300px;
     * 	<br>高度120px;
     * 	<br>默认 宋体
     * 	<br>水印横向50
     * 	<br>纵向30
     * 	<br>颜色默认灰色
     * 	
     * 	@author Pan
     * 	@param  width		宽度
     * 	@param  height		高度
     * 	@param  os			输出流
     * 	@param  arguments	输出参数
     * 	@return	boolean
     */
    public static boolean createWatermarkImage(int width, int height, OutputStream os, String... arguments) {
    	BufferedImage bufferedImage;
    	Graphics2D graphics2d = null;
    	BufferedImage transferAlpha;
    	try {
    		// 1.创建空白图片
    		bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    		// 2.获取图片画笔
    		graphics2d = bufferedImage.createGraphics();
    		// 3.设置画笔颜色
        	graphics2d.setColor(Color.white);
        	// 4.绘制矩形背景
        	graphics2d.fillRect(0, 0, width, height);
        	// 5.绘制矩形边框
        	graphics2d.setColor(Color.lightGray);
        	//	边框增加阴影虚化
        	//graphics2d.drawRect(0, 0, WIDTH, HEIGHT);
        	// 添加水印文字
        	// 6、处理文字
        	//AttributedString attributedString = new AttributedString(text);
        	//attributedString.addAttribute(TextAttribute.FONT, font, 0, text.length());
        	//AttributedCharacterIterator iter = attributedString.getIterator();
        	// 7、设置对线段的锯齿状边缘处理
        	graphics2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        	graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        	// 8、设置水印旋转
        	graphics2d.rotate(Math.toRadians(-10), (double) bufferedImage.getWidth() / 2, (double) bufferedImage.getHeight() / 2);
        	// 9、设置水印文字颜色
        	graphics2d.setColor(COLOR);
        	// 10、设置水印文字Font
        	graphics2d.setFont(FONT);
        	// 11、设置水印文字透明度
        	graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA));
        	graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        	 // 水印横向位置
            int positionWidth = 10;
            // 水印纵向位置
            int positionHeight = 30;
            // 12、水印位置
            createWatermarkImagePosition(width, height, graphics2d, positionWidth, positionHeight, arguments);
            transferAlpha = transferAlpha(bufferedImage);
		} finally {
			// 13、释放资源
			if (graphics2d != null) {
				graphics2d.dispose();
			}
		}
    	return write(transferAlpha, PNG_SUFFIX, os);
    }
    
    /**	
     * 	创建水印点
     * 
     * 	@author Pan
     * 	@param  graphics2d			图形2D对象
     * 	@param  positionWidth		定位宽度
     * 	@param  positionHeight		定位高度
     * 	@param  arguments			输出参数
     */
    private static void createWatermarkImagePosition(Graphics2D graphics2d, int positionWidth, int positionHeight, String... arguments) {
    	// 12、水印位置
    	for (int i = 0, len = arguments.length; i < len; i++) {
    		String text = arguments[i];
    		if (i == 0) {
    			graphics2d.drawString(text, positionHeight, positionWidth);
    			continue;
    		}
    		int x = positionHeight += 5;
    		int y = positionHeight += 25;
    		graphics2d.drawString(text, x, y);
    	}
    }
    
    /**	
     * 	创建水印点
     * 
     * 	@author Pan
     * 	@param  width				指定宽度
     * 	@param  height				指定高度
     * 	@param  graphics2d			图形2D对象
     * 	@param  positionWidth		定位宽度
     * 	@param  positionHeight		定位高度
     * 	@param  arguments			输出参数
     */
    private static void createWatermarkImagePosition(int width, int height, Graphics2D graphics2d, int positionWidth, int positionHeight, String... arguments) {
    	for (int i = 0, len = arguments.length; i < len; i++) {
    		String text = arguments[i];
    		if (i == 0) {
    			graphics2d.drawString(text, positionHeight, positionWidth);
    			continue;
    		}
    		int x = positionHeight += 5;
    		int y = positionHeight += 25;
    		graphics2d.drawString(text, x, y);
    	}
    }
    
    /**	
     *	转化成透明背景的图片
     *	 	
     * 	@author Pan
	 * 	@param  resourceImg    源图片
	 * 	@return	BufferedImage
	 */
	private static BufferedImage transferAlpha(BufferedImage resourceImg) {
		Image image = resourceImg;
		ImageIcon imageIcon = new ImageIcon(image);
		BufferedImage bufferedImage = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g2D = (Graphics2D) bufferedImage.getGraphics();
		g2D.drawImage(imageIcon.getImage(), 0, 0, imageIcon.getImageObserver());
		
		int alpha = 0;
		for (int j1 = bufferedImage.getMinY(); j1 < bufferedImage.getHeight(); j1++) {
			for (int j2 = bufferedImage.getMinX(); j2 < bufferedImage.getWidth(); j2++) {
				int rgb = bufferedImage.getRGB(j2, j1);
				int r = (rgb & 0xff0000) >> 16;
				int g = (rgb & 0xff00) >> 8;
				int b = (rgb & 0xff);
				if (((255 - r) < 30) && ((255 - g) < 30) && ((255 - b) < 30)) {
					rgb = ((alpha + 1) << 24) | (rgb & 0x00ffffff);
				}
				bufferedImage.setRGB(j2, j1, rgb);
			}
		}
		
		g2D.drawImage(bufferedImage, 0, 0, imageIcon.getImageObserver());
		// 直接输出文件用来测试结果
		return bufferedImage;
	}
	
	/**	
	 * 	读取图片
	 * 
	 * 	@author Pan
	 * 	@param  filePath 文件地址
	 * 	@return	BufferedImage
	 */
	public static BufferedImage read(String filePath) {
		return read(new File(filePath));
	}
	
	/**
	 * 	读取图片
	 * 
	 * 	@author Pan
	 * 	@param  file 文件对象
	 * 	@return	BufferedImage
	 */
	public static BufferedImage read(File file) {
		return readImage(file);
	}
	
	/**
	 * 	读取图片
	 * 
	 * 	@author Pan
	 * 	@param  is 输入流
	 * 	@return	BufferedImage
	 */
	public static BufferedImage read(InputStream is) {
		return readImage(is);
	}
	
	/**
	 * 	读取图片
	 * 
	 *  @author Pan
	 * 	@param  iis 图片输入流
	 * 	@return	BufferedImage
	 */
	public static BufferedImage read(ImageInputStream iis) {
		return readImage(iis);
	}
	
	/**
	 * 	读取图片
	 * 
	 * 	@author Pan
	 * 	@param  url url
	 * 	@return	BufferedImage
	 */
	public static BufferedImage read(URL url) {
		return readImage(url);
	}
	
	/**	
	 * 	避免抛出异常
	 * 
	 * 	@author Pan
	 * 	@param  obj	只能是File/InputStream/ImageInputStream/URL对象
	 * 	@return	BufferedImage
	 */
	private static BufferedImage readImage(Object obj) {
		try {
			if (obj instanceof File) {
				return ImageIO.read((File) obj);
			}
			if (obj instanceof InputStream) {
				return ImageIO.read((InputStream) obj);
			}
			if (obj instanceof ImageInputStream) {
				return ImageIO.read((ImageInputStream) obj);
			}
			if (obj instanceof URL) {
				return ImageIO.read((URL) obj);
			}
		} catch (Exception e) {
			throw new ImageReadException(e.getMessage(), e);
		}
		return null;
	}
	
    /**	
     * 	图片写入流
     * 	<br>需要手动关闭输出流
     * 	
     * 	@author Pan
     * 	@param  in				 图片输入流
     * 	@param  formatterImage   转换的图片格式
     * 	@param  os				 输出流
     * 	@return	boolean
     */
    public static boolean write(RenderedImage in, String formatterImage, OutputStream os) {
    	if (in == null) {
			return false;
		}
    	try {
    		return ImageIO.write(in, formatterImage, new BufferedOutputStream(os));
		} catch (Exception e) {
			throw new ImageWriteException(e.getMessage(), e);
		}
    }
    
    /**
     *  图片写入流
     * 	<br>默认PNG格式
     * 	<br>需要手动关闭输出流
     * 
     * 	@param 	in	图片输入流
     * 	@param 	os	输出流
     * 	@return	boolean
     */
    public static boolean write(RenderedImage in, OutputStream os) {
    	if (in == null) {
			return false;
		}
    	try {
    		return ImageIO.write(in, PNG_SUFFIX, new BufferedOutputStream(os));
		} catch (Exception e) {
			throw new ImageWriteException(e.getMessage(), e);
		}
    }
    
    /**
     * 	图片输出到本地目录下
     * 	<br>唯一ID序列.png
     * 	<br>默认格式PNG
     * 	<br>返回文件名
     * 	
     * 	@author Pan
     * 	@param  in			图片输入流
     * 	@param  filePath	输出到指定目录下
     * 	@return	boolean
     */
    public static String writeFile(RenderedImage in, String filePath) {
    	return writeFile(in, filePath, PNG_SUFFIX);
    }
    
    /**
     * 	图片输出到本地目录下
     * 	<br>唯一ID序列.xxx
     * 	<br>返回文件名
     * 		
     * 	@author Pan
     * 	@param  in			图片输入流
     * 	@param  filePath	输出到指定目录下
     * 	@param  suffix 		图片格式
     * 	@return	String 		文件名
     */
    public static String writeFile(RenderedImage in, String filePath, String suffix) {
    	if (in == null) {
    		return null;
    	}
		FileImageOutputStream fios = null;
		
		try {
			String createFileName = IdUtils.generateIdStr();
			if (suffix.startsWith(".")) {
				createFileName = createFileName.concat(suffix);
				suffix = suffix.substring(1);
			} else {
				createFileName = createFileName.concat(".").concat(suffix);
			}
			
			fios = new FileImageOutputStream(new File(filePath, createFileName));
			
			ImageIO.write(in, suffix, fios);
			return createFileName;
		} catch (Exception e) {
			throw new ImageWriteException(e.getMessage(), e);
		} finally {
			IOUtils.close(fios);
		}
    }
    
    /**	
     * 	jpg图片转换png
     * 	
     * 	@author Pan
     * 	@param  sourceImage	BufferedImage对象
     * 	@return	BufferedImage
     */
    public static BufferedImage convertPng(BufferedImage sourceImage) {
    	return sourceImage == null ? null : changeBitColor(sourceImage, 32);
    }
    
    /**
     * 	png转换jpg
     * 	
     * 	@author Pan
     * 	@param  sourceImage 原图片
     * 	@return	BufferedImage
     */
    public static BufferedImage convertJpg(BufferedImage sourceImage) {
    	return sourceImage == null ? null : changeBitColor(sourceImage, 24);
    }
    
    /**	
     * 	改变位图深度
     * 	目前支持改变24位和32位
     * 	
     * 	@author Pan
     * 	@param  sourceImage		原图片
     * 	@param  colorType		位图类型
     * 	@return	BufferedImage
     */
    private static BufferedImage changeBitColor(BufferedImage sourceImage, int colorType) {
    	if (sourceImage == null) {
    		return null;
    	}
    	//	判断图片位深度
		//in.getColorModel().getPixelSize();
    	BufferedImage targetBufferedImage;
    	switch (colorType) {
			case 24:
			case 32:
				targetBufferedImage = new BufferedImage(
	    			sourceImage.getWidth(), 
	    			sourceImage.getHeight(), 
	    			BufferedImage.TYPE_INT_RGB
		    	);
				break;
			default:
				return null;
		}
    	targetBufferedImage.createGraphics().drawImage(sourceImage, 0, 0, Color.white, null);
		return targetBufferedImage;
    }
    
    /**
     * 	获取文本长度
     * 
     * 	@author Pan
     * 	@param  text	内容
     * 	@return	int
     */
    public static int getTextLength(String text) {
        int length = text.length();
        
        for (int i = 0; i < text.length(); i++) {
            String str = String.valueOf(text.charAt(i));
           //	字节长度大于1，说明是中文，那么需要延长文本长度
            if (str.getBytes().length > 1) {
                length++;
            }
        }
        //	计算总共有多少个字节，也就是有多少个字
        length = (length % 2 == 0) ? length / 2 : length / 2 + 1;
        return length;
    }
}
